// Copyright (c) 2022 Presto Labs Pte. Ltd.
// Author: leon

#pragma once

#include <string>

#include <rapidjson/document.h>
#include <rapidjson/rapidjson.h>

#include "coin/proto/coin_market_enums.pb.h"
#include "coin2/base/conversion.h"
#include "coin2/base/time.h"
#include "coin2/exchange/base/feed/update.h"
#include "coin2/exchange/base/feed_util/book_builder.h"
#include "coin2/exchange/base/feed_util/trade.h"

namespace impl {
using coin2::exchange::base::market::ExchangeType;
using coin2::exchange::base::market::MarketType;

class MexcFuturesFeedUpdateParser {
 public:
  static void ParseSnapshot(
      BookBuilder* book_builder_,
      const rapidjson::Document& book_json) {
    int64_t ts = book_json["ts"].GetInt64() * 1'000'000LL;  // ms -> ns
    auto tx = book_builder_->NewTransaction(ts);
    tx.Clear();
    for (const auto& tick : book_json["data"]["asks"].GetArray()) {
      tx.UpdateAsk(tick[0].GetDouble(), tick[1].GetInt64());
    }
    for (const auto& tick : book_json["data"]["bids"].GetArray()) {
      tx.UpdateBid(tick[0].GetDouble(), tick[1].GetInt64());
    }
  }

  static void ParseIncremental(
      BookBuilder* book_builder_,
      const rapidjson::Document& book_json) {
    int64_t ts = book_json["ts"].GetInt64() * 1'000'000LL;  // ms -> ns
    auto tx = book_builder_->NewTransaction(ts);
    for (const auto& tick : book_json["data"]["asks"].GetArray()) {
      tx.UpdateAsk(tick[0].GetDouble(), tick[1].GetInt64());
    }
    for (const auto& tick : book_json["data"]["bids"].GetArray()) {
      tx.UpdateBid(tick[0].GetDouble(), tick[1].GetInt64());
    }
  }

  static void ParseTrade(
      const rapidjson::Value& trade_json,
      Trade* trade) {
    trade->timestamp = trade_json["t"].GetInt64() * 1'000'000LL;  // ms -> ns
    trade->side = ParseTradeSide(trade_json["T"].GetInt());
    trade->price = trade_json["p"].GetDouble();
    trade->fill_qty = trade_json["v"].GetInt64();
  }

 private:
  static coin2::exchange::feed::TradeSide ParseTradeSide(int side) {
    if (side == 1) {
      return coin2::exchange::feed::TRADE_BUY_SIDE;
    } else if (side == 2) {
      return coin2::exchange::feed::TRADE_SELL_SIDE;
    } else {
      throw std::runtime_error("[Mexc Futures Feed Processor] unknown side: " + side);
    }
  }
};
}  // namespace impl

using impl::MexcFuturesFeedUpdateParser;
